<!DOCTYPE html>



  


<html class="theme-next gemini use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
















  
  
  <link href="/blog/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />




  
  
  
  

  
    
    
  

  
    
      
    

    
  

  

  
    
      
    

    
  

  
    
      
    

    
  

  
    
    
    <link href="//fonts.googleapis.com/css?family=Monda:300,300italic,400,400italic,700,700italic|Roboto Slab:300,300italic,400,400italic,700,700italic|PT Mono:300,300italic,400,400italic,700,700italic|PT Mono:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
  






<link href="/blog/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/blog/css/main.css?v=5.1.4" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/blog/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/blog/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/blog/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/blog/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="Hexo, NexT" />










<meta name="description" content="JUST DO IT.">
<meta property="og:type" content="website">
<meta property="og:title" content="Srtian&#39;Blog">
<meta property="og:url" content="http://srtian96.gitee.io/blog/page/3/index.html">
<meta property="og:site_name" content="Srtian&#39;Blog">
<meta property="og:description" content="JUST DO IT.">
<meta property="og:locale" content="zh-Hans">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Srtian&#39;Blog">
<meta name="twitter:description" content="JUST DO IT.">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/blog/',
    scheme: 'Gemini',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="http://srtian96.gitee.io/blog/page/3/"/>





  <title>Srtian'Blog</title>
  








</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left 
  page-home">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/blog/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">Srtian'Blog</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">It's better to burn out than to fade away</p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/blog/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/blog/about/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br />
            
            关于
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/blog/tags/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            标签
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/blog/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/blog/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      

      
        <li class="menu-item menu-item-search">
          
            <a href="javascript:;" class="popup-trigger">
          
            
              <i class="menu-item-icon fa fa-search fa-fw"></i> <br />
            
            搜索
          </a>
        </li>
      
    </ul>
  

  
    <div class="site-search">
      
  <div class="popup search-popup local-search-popup">
  <div class="local-search-header clearfix">
    <span class="search-icon">
      <i class="fa fa-search"></i>
    </span>
    <span class="popup-btn-close">
      <i class="fa fa-times-circle"></i>
    </span>
    <div class="local-search-input-wrapper">
      <input autocomplete="off"
             placeholder="搜索..." spellcheck="false"
             type="text" id="local-search-input">
    </div>
  </div>
  <div id="local-search-result"></div>
</div>



    </div>
  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            
  <section id="posts" class="posts-expand">
    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/07/26/Node.js爬虫初体验/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/07/26/Node.js爬虫初体验/" itemprop="url">Node.js 爬虫初体验</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-07-26T16:15:31+08:00">
                2018-07-26
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/Node-js/" itemprop="url" rel="index">
                    <span itemprop="name">Node.js</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  952
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  4
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="一、准备阶段"><a href="#一、准备阶段" class="headerlink" title="一、准备阶段"></a>一、准备阶段</h3><p>当我们需要使用Node.js进行爬虫爬取网页时，我们通常需要下载两个库request和cheerio来帮助我们队网页进行爬取：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cnpm i request cheerio</span><br></pre></td></tr></table></figure></p>
<p>其中request帮助我们对网页进行加载，而cheerio则是为服务器特别定制的，快速、灵活、实施的jQuery核心实现。有了这两个库，爬取简单的网页就没有太大的问题了。</p>
<h3 id="二、网页分析"><a href="#二、网页分析" class="headerlink" title="二、网页分析"></a>二、网页分析</h3><p>本次我的目标是爬取豆瓣电影Top250的第一页，因此打开浏览器对其页面结构进行了一番分析，就比如第一步电影——《肖申克的救赎》：<br><img src="https://images.gitee.com/uploads/images/2018/0726/154313_2415be33_1575229.png" alt="输入图片说明" title="页面结构.png"></p>
<p>通过上面的页面结构，我们不难看出，每一部电影都是一个li，且li下面的class都是item,因此当我们需要爬取一部电影的数据时，可以先取得item，再对内部的数据进行获取。其次，每部电影的数据的class命名很明确，因此当我们获取数据时，直接可以根据页面的class命名进行数据获取。而根据对页面的分析，我打算爬取的数据如下：</p>
<ul>
<li>电影名称——name</li>
<li>评分——score</li>
<li>评语——quote</li>
<li>排名——ranking</li>
<li>封面地址——coverUrl</li>
</ul>
<h3 id="三、代码编写"><a href="#三、代码编写" class="headerlink" title="三、代码编写"></a>三、代码编写</h3><p>既然确定所要获取的数据，我们就可以着手写代码了，首先我们需要引入上面我们下载好的两个包：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> request = <span class="built_in">require</span>(<span class="string">'request'</span>)</span><br><span class="line"><span class="keyword">const</span> cheerio = <span class="built_in">require</span>(<span class="string">'cheerio'</span>)</span><br></pre></td></tr></table></figure>
<p>然后我们要创造一个类，用以保存我们想要获取的数据：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> Movie = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.name = <span class="string">''</span></span><br><span class="line">    <span class="keyword">this</span>.score = <span class="number">0</span></span><br><span class="line">    <span class="keyword">this</span>.quote = <span class="string">''</span></span><br><span class="line">    <span class="keyword">this</span>.ranking = <span class="number">0</span></span><br><span class="line">    <span class="keyword">this</span>.coverUrl = <span class="string">''</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>然后我们就能根据我们在上面所创造的类以及利用cheerio来定义一个函数，来通过传入的元素对数据进行获取：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> getMovieFromDiv = <span class="function">(<span class="params">div</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> movie = <span class="keyword">new</span> Movie()</span><br><span class="line">    <span class="keyword">const</span> load = cheerio.load(div)</span><br><span class="line">    <span class="keyword">const</span> pic = load(<span class="string">'.pic'</span>)</span><br><span class="line">    movie.name = load(<span class="string">'.title'</span>).text()</span><br><span class="line">    movie.score = load(<span class="string">'.rating_num'</span>).text()</span><br><span class="line">    movie.quote = load(<span class="string">'.inq'</span>).text()</span><br><span class="line">    movie.ranking = pic.find(<span class="string">'em'</span>).text()</span><br><span class="line">    movie.coverUrl = pic.find(<span class="string">'img'</span>).attr(<span class="string">'src'</span>)</span><br><span class="line">    <span class="keyword">return</span> movie</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>将数据获取到了后，当然就需要将其保存了，我们可以调用Node.js的fs模块来对数据进行保存。在这里我们同样也可以定义一个函数，用以保存数据，鉴于在前端界数据通常是JSON，因此在这里就将数据保存为JSON格式：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> saveMovie = <span class="function">(<span class="params">movies</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>)</span><br><span class="line">    <span class="keyword">const</span> path = <span class="string">'DouBanTop25.json'</span></span><br><span class="line">    <span class="keyword">const</span> s = <span class="built_in">JSON</span>.stringify(movies, <span class="literal">null</span>, <span class="number">2</span>)</span><br><span class="line">    fs.writeFile(path, s, (error) =&gt; &#123;</span><br><span class="line">        <span class="keyword">if</span> (error === <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">'保存成功'</span>)</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">'保存文件错误'</span>, error)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>好了，上面两步主要为了处理数据以及保存数据。下面就是主要部分了，我们需要下载页面，并执行上面两个函数，已达到爬取网页数据并保存的目的：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> getMoviesFromUrl = <span class="function">(<span class="params">url</span>) =&gt;</span> &#123;</span><br><span class="line">    request(url, (error, response, body) =&gt; &#123;</span><br><span class="line">        <span class="keyword">if</span> (error === <span class="literal">null</span> &amp;&amp; response.statusCode == <span class="number">200</span>) &#123;</span><br><span class="line">            <span class="keyword">const</span> load = cheerio.load(body)</span><br><span class="line">            <span class="keyword">const</span> movieDiv = load(<span class="string">'.item'</span>)</span><br><span class="line">            <span class="keyword">const</span> movies = []</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; movieDiv.length; i++) &#123;</span><br><span class="line">                <span class="keyword">let</span> element = movieDiv[i]</span><br><span class="line">                <span class="keyword">const</span> div = load(element).html()</span><br><span class="line">                <span class="keyword">const</span> movie = getMovieFromDiv(div)</span><br><span class="line">                movies.push(movie)</span><br><span class="line">            &#125;</span><br><span class="line">            saveMovie(movies)</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">'请求失败'</span>, error)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在上面，当我们下载好页面后，先利用cheerio.load解析页面，然后我们创建一个数组，用于保存电影的数据。再然后通for循环遍历页面的item，并通过getMovieFromDiv来对每个item内的数据进行获取，然后push到数组内。在循环结束后，使用saveMovie将存有数据的数组进行保存。</p>
<p>基本的爬取数据的代码完成了，现在让我们来启动这些函数进行页面爬取吧！</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> getMovie = <span class="function"><span class="params">()</span> =&gt;</span>&#123;</span><br><span class="line">    <span class="keyword">const</span> url = <span class="string">'https://movie.douban.com/top250'</span></span><br><span class="line">    getMoviesFromUrl(url)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">getMovie()</span><br></pre></td></tr></table></figure>
<p>最后：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">node doubantop25.js</span><br></pre></td></tr></table></figure>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/07/21/初探 HTML5 Web Workers/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/07/21/初探 HTML5 Web Workers/" itemprop="url">初探 HTML5 Web Workers</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-07-21T13:35:33+08:00">
                2018-07-21
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/JavaScript/" itemprop="url" rel="index">
                    <span itemprop="name">JavaScript</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  2,253
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  9
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>这段时间一这被很多事情所牵绕，没能好好的写博客学习，感觉很难受，但所幸事情大都告一段落了。对于 Web Workers 的学习主要是由于在上这周面试的时候，面试的一个大佬，问到了这个问题，而我只知道它有这个东西，但具体如何实现它，以及它的使用场景我不是很清楚。因此这两天花了些时间看了不少文章，来总结一下。</p>
<h3 id="一、Web-Workers是什么"><a href="#一、Web-Workers是什么" class="headerlink" title="一、Web Workers是什么"></a>一、Web Workers是什么</h3><blockquote>
<p>Web Worker为Web内容在后台线程中运行脚本提供了一种简单的方法。线程可以执行任务而不干扰用户界面。此外，他们可以使用XMLHttpRequest执行 I/O (尽管responseXML和通道属性总是为空)。一旦创建， 一个worker 可以将消息发送到创建它的JavaScript代码, 通过将消息发布到该代码指定的事件处理程序 (反之亦然)。 —— MDN</p>
</blockquote>
<p>众所周知，JavaScript是单线程的编程语言，也就是说，当我们在页面中进行一个较为耗时的计算的JavaScript代码时，在这段代码执行完毕之前，页面是无法响应用户操作的。也正是出于这个原因，HTML5为我们提供了 Web Workers 以解决这种问题，当我们需要在JavaScript中来进行耗时的计算或诸如此类的问题时，我们可以使用 Web Workers 在浏览器的后台启动一个独里的 Worker 线程来专门负责这段代码的运行，而不会阻碍后面代码的运行。</p>
<h3 id="二、Web-Workers的使用"><a href="#二、Web-Workers的使用" class="headerlink" title="二、Web Workers的使用"></a>二、Web Workers的使用</h3><h4 id="1-实例化一个-Worker"><a href="#1-实例化一个-Worker" class="headerlink" title="1. 实例化一个 Worker"></a>1. 实例化一个 Worker</h4><p>实例化运行一个 Worker 很简单，我们只需要 new 一个 Worker 全局对象即可。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> worker = <span class="keyword">new</span> Worker(<span class="string">'./worker.js'</span>)</span><br></pre></td></tr></table></figure>
<p>它接受一个 filepathname String 参数，用于指定 Worker 脚本文件的路径。然后我们就可以在 worker.js 中写下一些代码:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(<span class="string">'my_WOEKER:'</span>, <span class="string">'srtian'</span>)</span><br></pre></td></tr></table></figure>
<p>另外，通过URL.createObjectURL()创建URL对象，也可以实现创建内嵌的worker:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myTask = <span class="string">`</span></span><br><span class="line"><span class="string">    var i = 0;</span></span><br><span class="line"><span class="string">    var timedCount = () =&gt; &#123;</span></span><br><span class="line"><span class="string">        i = i+1;</span></span><br><span class="line"><span class="string">        postMessage(i);</span></span><br><span class="line"><span class="string">        setTimeout(timedCount, 1000);</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string">    timedCount();</span></span><br><span class="line"><span class="string">`</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> myblob = <span class="keyword">new</span> Blob([myTask]);</span><br><span class="line"><span class="keyword">var</span> myWorker = <span class="keyword">new</span> Worker(<span class="built_in">window</span>.URL.createObjectURL(myblob));</span><br></pre></td></tr></table></figure>
<blockquote>
<p>需要注意的是，传入 Worker 构造函数的参数 URI 必须遵循同源策略。</p>
</blockquote>
<p>此外因为Worker线程的创建的是异步的，所以主线程代码不会阻塞在这里等待 worker 线程去加载、执行指定的脚本文件，而是会立即向下继续执行后面代码这点也需要注意。</p>
<h4 id="2-数据通信"><a href="#2-数据通信" class="headerlink" title="2. 数据通信"></a>2. 数据通信</h4><p>当我们实例化一个 Worker 线程后，Worker不会相互，或者与主程序共享任何作用域或资源——那会将所有的多线程编程的噩梦带到我们面前——取而代之的是一种连接它们的基本事件消息机制。因此他们需要通过基于事件监听机制的message来进行通信，我们在new Worker()后悔返回一个实例对象，它包含了一个postMessage的方法，我们可以通过调用这个方法来给worker线程传递信息，我们也可以给这个对象监听事件，从而在worker线程中出发事件通信的时候能接收到数据。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> worker = <span class="keyword">new</span> worker(<span class="string">'./worker.js'</span>)</span><br><span class="line">worker.addEventListener(<span class="string">'message'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'worker receive:'</span>, e.data )</span><br><span class="line">&#125;</span><br><span class="line">worker.postMessage(<span class="string">'hello worker,this is main.js'</span>)</span><br></pre></td></tr></table></figure>
<p>然后在worker.js这个脚本中，我们就可以调用全局函数postMessage和全局的onmessage赋值来发送和监听数据和事件了。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 监听事件</span></span><br><span class="line">onmessage = <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'WORKER RECEIVE：'</span>, e.data);</span><br><span class="line">  <span class="comment">// 发送数据事件</span></span><br><span class="line">  postMessage(<span class="string">'Hello, this is worker.js'</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>需要注意的是 worker 支持 JavaScript 中所有类型的数据传递，可以传递一个 Object 数据；但这里的数据传递（主要是 Object 类型）并不是共享，而是复制。发送端的数据和接收端的数据是复制而来，并不指向同一个对象，此外这里的复制不是简单的拷贝，而是通过两端的序列化/解序列化来实现的，一般来说浏览器会通过 JSON 编码/解码；当然，这里的更多细节部分会由浏览器来处理，我们并不需要关心这些，只需要明白两端的数据是复制而来，互相独立的就行了。</p>
<h4 id="3-错误处理机制"><a href="#3-错误处理机制" class="headerlink" title="3. 错误处理机制"></a>3. 错误处理机制</h4><p>当 worker 出现运行中错误时，它的 onerror 事件处理函数会被调用。它会收到一个扩展了 ErrorEvent 接口的名为 error的事件。</p>
<p>该事件不会冒泡并且可以被取消；为了防止触发默认动作，worker 可以调用错误事件的 preventDefault() 方法。</p>
<p>错误事件有以下三个用户关心的字段：</p>
<ul>
<li>message: 可读性良好的错误消息。</li>
<li>filename: 发生错误的脚本文件名。</li>
<li>lineno: 发生错误时所在脚本文件的行号。 </li>
</ul>
<p>实际操作如下：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> worker = <span class="keyword">new</span> Worker(<span class="string">'./worker.js'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 监听消息事件</span></span><br><span class="line">worker.addEventListener(<span class="string">'message'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'MAIN RECEIVE： '</span>, e.data);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">// 也可以使用 onMessage 来监听事件：</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 监听 error 事件</span></span><br><span class="line">worker.addEventListener(<span class="string">'error'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'MAIN ERROR：'</span>, e);</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'MAIN ERROR：'</span>, <span class="string">'filename:'</span> + e.filename + <span class="string">'---message:'</span> + e.message + <span class="string">'---lineno:'</span> + e.lineno);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 触发事件，传递信息给 Worker</span></span><br><span class="line">worker.postMessage(&#123;</span><br><span class="line">  m: <span class="string">'Hello Worker, this is main.js'</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h4 id="4-终止-Worker"><a href="#4-终止-Worker" class="headerlink" title="4. 终止 Worker"></a>4. 终止 Worker</h4><p>当我们在不需要 Worker 继续运行时，我就需要终止掉这个线程，这时候我们就可以调用 worker 的 terminate 方法:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">worker.terminate()</span><br></pre></td></tr></table></figure>
<p>worker 线程会被立即杀死，不会有任何机会让它完成自己的操作或清理工作。</p>
<p>而在worker线程中，workers 也可以调用自己的 close  方法进行关闭：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">close()</span><br></pre></td></tr></table></figure>
<h3 id="三-Web-Workers的兼容"><a href="#三-Web-Workers的兼容" class="headerlink" title="三. Web Workers的兼容"></a>三. Web Workers的兼容</h3><p>由于Web Workers是HTML5所提供的，因此从兼容性上来说，还是需要注意的。总的兼容情况如下图所示：<br><img src="https://qiutc.me/img/section-webworker-1.png" alt="image"><br>图片来源：<a href="https://caniuse.com/#feat=webworkers" target="_blank" rel="noopener">https://caniuse.com/#feat=webworkers</a></p>
<p>我们可以看到，虽然web worker很不错，但如果我们的代码执行在较老的浏览器中时，是缺乏支持的。但由于worker是一个API而不是语法，因此我门还是可以去填补它的。</p>
<p>这一块的详情可以去看——《你不知道的JavaScript中卷》关于 web worker 的那一节。</p>
<h3 id="四、Web-Workers支持的JavaScript特性"><a href="#四、Web-Workers支持的JavaScript特性" class="headerlink" title="四、Web Workers支持的JavaScript特性"></a>四、Web Workers支持的JavaScript特性</h3><p>由于在 Worker 线程的运行环境中没有 window 全局对象，也无法访问 DOM 对象，所以一般来说我们在这只能执行纯JavaScript的计算操作，当然1我们那：</p>
<ul>
<li>setTimeout()， clearTimeout()， setInterval()， clearInterval()：有了设计个函数，就可以在 Worker 线程中执行定时操作了；</li>
<li>XMLHttpRequest 对象：意味着我们可以在 Worker 线程中执行 ajax 请求；</li>
<li>navigator 对象：可以获取到 ppName，appVersion，platform，userAgent 等信息；</li>
<li>location 对象（只读）：可以获取到有关当前 URL 的信息；</li>
<li>应用缓存</li>
<li>使用 importScripts() 引入外部 script</li>
<li>创建其他的 Web Worker</li>
</ul>
<h3 id="五、Web-Worker-的实践"><a href="#五、Web-Worker-的实践" class="headerlink" title="五、Web Worker 的实践"></a>五、Web Worker 的实践</h3><p>总的来说，Web Worker为我们带来了强大的计算能力，我们可以加载一个JavaScript进行大量的复杂计算，而用不挂起主进程。并通过postMessage，onmessage进行通信，这也解决了大量计算对UI渲染的阻塞问题。</p>
<h4 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h4><h5 id="1、数学运算"><a href="#1、数学运算" class="headerlink" title="1、数学运算"></a>1、数学运算</h5><p>Web Worker最简单的应用应该就是用来进行后台计算了，这对CPU密集型的场景再适合不过了。</p>
<h5 id="2、图像处理"><a href="#2、图像处理" class="headerlink" title="2、图像处理"></a>2、图像处理</h5><p>通过使用从 canvas 中获取的数据，可以把图像分割成几个不同的区域并且把它们推送给并行的不同Workers来做计算，对图像进行像素级的处理，再把处理完成的图像数据返回给主页面。</p>
<h5 id="3、大数据的处理"><a href="#3、大数据的处理" class="headerlink" title="3、大数据的处理"></a>3、大数据的处理</h5><p>目前mvvm框架越来越普及，基于数据驱动的开发模式也越愈发流行，未来大数据的处理也可能转向到前台，因此我们将大数据的处理交给在Web Worker也是很好的。</p>
<h5 id="4-数据预处理"><a href="#4-数据预处理" class="headerlink" title="4. 数据预处理"></a>4. 数据预处理</h5><p>为优化的网站或 web 应用的数据加载时长，我们可以使用 Web Worker 预先获取一些数据，存储起来以备后续使用，因为它绝不会影响应用的 UI 体验。</p>
<h5 id="5-大量的-ajax-请求或者网络服务轮询"><a href="#5-大量的-ajax-请求或者网络服务轮询" class="headerlink" title="5. 大量的 ajax 请求或者网络服务轮询"></a>5. 大量的 ajax 请求或者网络服务轮询</h5><p>由于在主线程中每启动一个XMLHttpRequest请求都会消耗资源，虽然在请求过程中浏览器另外开了一个线程，但是在交互过程中还是需要消耗主线程资源；而使用worker则不会过多占用主线程，只是启动worker过程时比较耗资源。</p>
<p>参考资料：</p>
<ol>
<li>《你不知道的JavaScript中卷》</li>
<li><a href="https://juejin.im/post/59c1b3645188250ea1502e46" target="_blank" rel="noopener">https://juejin.im/post/59c1b3645188250ea1502e46</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API/Using_web_workers" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API/Using_web_workers</a></li>
<li><a href="https://qiutc.me/post/the-multithread-in-javascript-web-worker.html" target="_blank" rel="noopener">https://qiutc.me/post/the-multithread-in-javascript-web-worker.html</a></li>
<li><a href="https://juejin.im/post/5a90233bf265da4e92683de3" target="_blank" rel="noopener">https://juejin.im/post/5a90233bf265da4e92683de3</a></li>
</ol>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/07/04/Node.js模块之Buffer/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/07/04/Node.js模块之Buffer/" itemprop="url">Node.js模块之Buffer</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-07-04T13:55:18+08:00">
                2018-07-04
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/Node-js/" itemprop="url" rel="index">
                    <span itemprop="name">Node.js</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  945
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  4
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="Buffer的简单介绍"><a href="#Buffer的简单介绍" class="headerlink" title="Buffer的简单介绍"></a>Buffer的简单介绍</h3><p>由于在Node中，我们的应用需要处理网络协议、操作数据库、处理图片、接收上传文件等，在网络流和文件的操作中，还要处理大量二进制数据。而JavaScript原生方法，并不能满足这些需求，出于这些需求Buffer 也就应运而生了。<br>Buffer是Node.js中非常重要的一个模块，<br>Node.js的官方文档是这么介绍Buffer的：<a href="https://nodejs.org/api/buffer.html#buffer_buffer" target="_blank" rel="noopener">Node.js_Buffer</a> :</p>
<blockquote>
<p>Prior to the introduction of TypedArray, the JavaScript language had no mechanism for reading or manipulating streams of binary data. The Buffer class was introduced as part of the Node.js API to enable interaction with octet streams in TCP streams, file system operations, and other contexts.</p>
</blockquote>
<blockquote>
<p>With TypedArray now available, the Buffer class implements the Uint8Array API in a manner that is more optimized and suitable for Node.js.</p>
</blockquote>
<blockquote>
<p>Instances of the Buffer class are similar to arrays of integers but correspond to fixed-sized, raw memory allocations outside the V8 heap. The size of the Buffer is established when it is created and cannot be changed.</p>
</blockquote>
<blockquote>
<p>The Buffer class is within the global scope, making it unlikely that one would need to ever use require(‘buffer’).Buffer.</p>
</blockquote>
<p>简单来说：</p>
<ul>
<li>Buffer 库就是为 Node.js 带来<br>了一种存储原始数据的方法，从而可以让 Nodejs 处理二进制数据。</li>
<li>每当我们需要在 Nodejs 中可以使用 Buffer 来处理诸如TCP流，文件系统操作和其他上下文中的移动的数据。原始数据存储在 Buffer 类的实例中。</li>
<li>Buffer 类似于一个整数数组，但它对应于 V8 堆内存之外的一块原始内存，且创建后无法更改。</li>
<li>由于 Buffer 是一个全局范围定义的，所以我们无需引用就可以使用。</li>
</ul>
<p>值得注意的是Buffer是一个典型的JavaScript与 C++ 结合的模块。它将性能相关部分用 C++ 实现，将非性能相关的部分则用 JavaScript 实现。<br>前面有提到 Buffer 所占的内存不是由V8分配的，而是独立于 V8 堆内存之外，通过 C++ 来实现内存申请、javascript 分配内存。每当我们使用Buffer.alloc(size)请求一个Buffer内存时，Buffer会以8KB为界限来判断分配的是大对象还是小对象，小对象存入剩余内存池，不够再申请一个8KB的内存池；大对象直接采用C++层面申请的内存。因此，对于一个大尺寸对象，申请一个大内存比申请众多小内存池快很多。</p>
<h3 id="Buffer的简单使用"><a href="#Buffer的简单使用" class="headerlink" title="Buffer的简单使用"></a>Buffer的简单使用</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建一个长度为 10、且用 20 填充的 Buffer。</span></span><br><span class="line"><span class="keyword">const</span> buf1 = Buffer.alloc(<span class="number">10</span>, <span class="number">20</span>)</span><br><span class="line"><span class="built_in">console</span>.log(buf1)</span><br><span class="line"><span class="comment">// &lt;Buffer 14 14 14 14 14 14 14 14 14 14&gt;</span></span><br></pre></td></tr></table></figure>
<p>Buffer同样也可以与string互相转化：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> buf2 = Buffer.from(<span class="string">'javascript'</span>)</span><br><span class="line"><span class="built_in">console</span>.log(buf2)</span><br><span class="line"><span class="comment">// &lt;Buffer 6a 61 76 61 73 63 72 69 70 74&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>需要注意的是，Buffer.from不支持传入数字：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Buffer.from(<span class="number">12345</span>)</span><br><span class="line"><span class="comment">// TypeError [ERR_INVALID_ARG_TYPE]: The "value" argument must not be of type number. Received type number at Function.from (buffer.js:215:11)</span></span><br></pre></td></tr></table></figure>
<p>因此如果我们需要传入数字，可以将其以数组的形式传进去：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> buf = Buffer.from([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>]);</span><br><span class="line"><span class="built_in">console</span>.log(buf); <span class="comment">//  &lt;Buffer 01 02 03 04 05&gt;</span></span><br></pre></td></tr></table></figure>
<p>但是这种方式存在一个问题，当存入不同的数值的时候buffer中记录的二进制数据会相同，如下所示：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> buf1 = Buffer.from([<span class="number">127</span>, <span class="number">-1</span>])</span><br><span class="line"><span class="built_in">console</span>.log(buf1)</span><br><span class="line"><span class="comment">// &lt;Buffer 7f ff&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> buf2 = Buffer.from([<span class="number">127</span>, <span class="number">255</span>])</span><br><span class="line"><span class="built_in">console</span>.log(buf2) </span><br><span class="line"><span class="comment">// &lt;Buffer 7f ff&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(buf2.equals(buf1))  <span class="comment">// true</span></span><br></pre></td></tr></table></figure>
<p>当要记录的一组数全部落在0到255（readUInt8来读取）这个范围, 或者全部落在-128到127（readInt8来读取）这个范围那么就没有问题，否则的话就强烈不推荐使用Buffer.from来保存一组数。因为不同的数字读取时应该调用不同的方法。</p>
<p>参考文档：</p>
<ul>
<li>《深入浅出Node.js》</li>
<li><a href="https://juejin.im/post/5afd57e851882542ac7d76af" target="_blank" rel="noopener">https://juejin.im/post/5afd57e851882542ac7d76af</a></li>
<li><a href="http://nodejs.cn/api/buffer.html#buffer_buffer" target="_blank" rel="noopener">http://nodejs.cn/api/buffer.html#buffer_buffer</a></li>
</ul>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/07/01/浏览器的多进程学习笔记/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/07/01/浏览器的多进程学习笔记/" itemprop="url">浏览器多线程的基础知识</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-07-01T13:20:43+08:00">
                2018-07-01
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/浏览器/" itemprop="url" rel="index">
                    <span itemprop="name">浏览器</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  1,070
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  4
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="浏览器的多进程"><a href="#浏览器的多进程" class="headerlink" title="浏览器的多进程"></a>浏览器的多进程</h3><p>要想搞明白什么是浏览器的多进程，首先得知道什么是进程。按照维基百科的说法：</p>
<blockquote>
<p>进程是计算机中已运行程序的实体。进程是线程的容器，进程本身不运行。程序本身只是指令的集合，进程才是程序（指令）的真正运行。每个程序可以有多个进程，每个进程都有自己的资源。</p>
</blockquote>
<p>简单来讲，进程就是CPU资源分配的最小单位，而线程则是CPU调度的最小单位。</p>
<p>而我们很早就知道浏览器是多进程的，浏览器之所以能够运行，就是因为系统给它的进程分配了资源。也就是说，我们每打开一个Tab页，就相当于创建了一个独立的浏览器进程。那么浏览器多线程的优势有哪些呢，据我在网上所看到的说法，感觉挺有道理的主要是以下四个：</p>
<ol>
<li>避免当个page crash影响整个浏览器</li>
<li>避免第三方插件crash 影响整个浏览器</li>
<li>多线程充分利用了多核的优势</li>
<li>方便使用沙盒模型隔离插件等进程，以提高浏览器的稳定性</li>
</ol>
<p>简单来说：浏览器的多进程就是防止一个Tab页面崩溃了而导致整个浏览器的崩溃。</p>
<p>既然浏览器是多进程的，那么这个多进程中的诸多进程，肯定都有着自己各自的类型了。总的来说浏览器的主要进程类型有以下几种：</p>
<ol>
<li>Browser进程：这是浏览器的主进程。有且只有一个，它主要有以下几个作用：</li>
</ol>
<ul>
<li>负责浏览器页面的显示与页面交互</li>
<li>负责个页面的管理。创建和销毁其他进程</li>
<li>将 Renderer 进程得到的内存中的 Bitmap ，绘制到用户页面上。</li>
<li>网络资源的管理，下载等。</li>
</ul>
<ol start="2">
<li>第三方插件进程：每种类型的插件都对应着一个进程，只有当使用该插件时才会创建该进程</li>
<li>GPU进程：最多一个，用于3D绘制</li>
<li>浏览器渲染进程，也就是浏览器内核，Renderer进程，内部是多线程的：默认每个Tab页面都是一个进程，互相不影响。主要作用是页面渲染，脚本执行，事件处理等。</li>
</ol>
<h3 id="浏览器渲染进程"><a href="#浏览器渲染进程" class="headerlink" title="浏览器渲染进程"></a>浏览器渲染进程</h3><p>前面我们曾提到浏览器渲染进程，我以前也写过一篇文章，专门来讨论浏览器的渲染原理及过程。毫无疑问的是，浏览器的渲染进程是非常重要的进程，因为页面的渲染，JavaScript的执行，事件的处理等都需要在这个进程里进行，因此这个进程我们得好好的来掌握它。</p>
<p>首先我们要知道的是，浏览器的渲染继承是多线程的，它主要包含以下几种常驻线程：</p>
<ul>
<li>GUI渲染线程：主要负责渲染浏览器页面。此外当页面进行Repaint和reflow时，该线程也会执行。需要注意的是GUI渲染线程与JavaScript引擎是互斥的，当JavaScript引擎执行时，GUI线程就会被挂起，GUI更新会被把被保存在一个队列中，等JavaScript引擎空闲时立即执行。</li>
<li>JavaScript引擎线程：通常也被我们称为JavaScript内核，负责处理JavaScript脚本程序，运行代码。比如大名鼎鼎的V8引擎。JavaScript引擎会一直等待着任务队列的到来，然后加以处理，一个Tab页中无论何时都只有一个JavaScript线程在运行JavaScript程序。</li>
<li>事件触发线程：它归属于浏览器而不是JavaScript引擎，用于控制事件循环。</li>
<li>定时触发器线程：也就是 setInterval 与 setTimeout 所在线程</li>
<li>异步http请求线程：在XMLHttpRequest在连接后通过浏览器新开一个线程请求，<br>可以检测到状态的变更。如果设置有回调函数，异步线程就产生状态变更事件，并将这个回调再放入事件队列中，最后由JavaScript引擎执行。</li>
</ul>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/06/27/Koa2 初体验（二）/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/06/27/Koa2 初体验（二）/" itemprop="url">Koa2初体验(二)</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-06-27T14:47:43+08:00">
                2018-06-27
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/Node-js/" itemprop="url" rel="index">
                    <span itemprop="name">Node.js</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  595
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  3
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="Koa2-路由"><a href="#Koa2-路由" class="headerlink" title="Koa2 路由"></a>Koa2 路由</h3><h4 id="Koa2-原生路由的实现"><a href="#Koa2-原生路由的实现" class="headerlink" title="Koa2 原生路由的实现"></a>Koa2 原生路由的实现</h4><p>路由在web中的作用不言而喻，而要先实现原生路由，需要的到地址栏输入的路径，然后再根据路径不同进行跳转。而在Koa2中，我们可以用 ctx.requerst.url 来实现获取访问路径：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> Koa = <span class="built_in">require</span>(<span class="string">'koa'</span>)</span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Koa()</span><br><span class="line"></span><br><span class="line">app.use(<span class="keyword">async</span>(ctx) =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> url = ctx.request.url</span><br><span class="line">    ctx.body = url </span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">app.listen(<span class="number">3000</span>, () =&gt; &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'demo3 is run'</span>)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></p>
<p>加入我们的文件结构是这样的：</p>
<pre><code>├── demo3.js
├── package.json
└── view
    ├── register.html
    ├── index.html
    └── login.html
</code></pre><p>我们就可以这样来实现原生路由：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> Koa = <span class="built_in">require</span>(<span class="string">'koa'</span>)</span><br><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>)</span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Koa()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">render</span>(<span class="params">page</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">let</span> viewUrl = <span class="string">`./view/<span class="subst">$&#123;page&#125;</span>`</span></span><br><span class="line">        fs.readFile(viewUrl, <span class="string">"binary"</span>, (err, data) =&gt; &#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="number">1</span>)</span><br><span class="line">            <span class="keyword">if</span> (err) &#123;</span><br><span class="line">                reject(err)</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                resolve(data)</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">route</span>(<span class="params">url</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> view = <span class="string">'404.html'</span></span><br><span class="line">    <span class="keyword">switch</span>(url) &#123;</span><br><span class="line">        <span class="keyword">case</span> <span class="string">'/'</span>:</span><br><span class="line">            view = <span class="string">'index.html'</span></span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        <span class="keyword">case</span> <span class="string">'/login'</span>:</span><br><span class="line">            view = <span class="string">'login.html'</span></span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        <span class="keyword">case</span> <span class="string">'/register'</span>:</span><br><span class="line">            view = <span class="string">'register.html'</span></span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        <span class="keyword">case</span> <span class="string">'/index'</span>:</span><br><span class="line">            view = <span class="string">'index.html'</span></span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        <span class="keyword">default</span>:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">let</span> html = <span class="keyword">await</span> render(view)</span><br><span class="line">    <span class="keyword">return</span> html</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">app.use(<span class="keyword">async</span>(ctx) =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> url = ctx.request.url</span><br><span class="line">    <span class="keyword">let</span> html = <span class="keyword">await</span> route(url)</span><br><span class="line">    ctx.body = html</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">app.listen(<span class="number">3000</span>, () =&gt; &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'demo3 is run'</span>)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<p>通过上面的代码，我们成功实现了一个路由切换的功能，但这样写无疑是不够优雅的，且也只是在原理上的实现，不足以应付我们日常开发中所遇到的种种问题。因此我们和上次一样，还是需要引入中间件来达成我们的目标。</p>
<h4 id="koa-router"><a href="#koa-router" class="headerlink" title="koa-router"></a>koa-router</h4><p>首先我们需要下载 koa-router 中间件：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cnpm i koa-router --save</span><br></pre></td></tr></table></figure>
<p>然后我们就能通过koa-router来优雅的进行路由调换了：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> Koa = <span class="built_in">require</span>(<span class="string">'koa'</span>)</span><br><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>)</span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Koa()</span><br><span class="line"><span class="keyword">const</span> Router = <span class="built_in">require</span>(<span class="string">'koa-router'</span>)</span><br><span class="line"><span class="keyword">let</span> home = <span class="keyword">new</span> Router()</span><br><span class="line"></span><br><span class="line">home.get(<span class="string">'/'</span>, <span class="keyword">async</span> ( ctx ) =&gt; &#123;</span><br><span class="line">    <span class="keyword">let</span> html = <span class="string">`</span></span><br><span class="line"><span class="string">        &lt;ul&gt;</span></span><br><span class="line"><span class="string">            &lt;li&gt;&lt;a href="/page/helloworld"&gt;/page/helloworld&lt;/a&gt;&lt;/li&gt;</span></span><br><span class="line"><span class="string">            &lt;li&gt;&lt;a href="/page/404"&gt;/page/404&lt;/a&gt;&lt;/li&gt;</span></span><br><span class="line"><span class="string">        &lt;/ul&gt;</span></span><br><span class="line"><span class="string">    `</span></span><br><span class="line">    ctx.body = html</span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment">// 子路由2</span></span><br><span class="line"><span class="keyword">let</span> page = <span class="keyword">new</span> Router()</span><br><span class="line">page.get(<span class="string">'/404'</span>, <span class="keyword">async</span> ( ctx )=&gt;&#123;</span><br><span class="line">    ctx.body = <span class="string">'404 page!'</span></span><br><span class="line">&#125;).get(<span class="string">'/helloworld'</span>, <span class="keyword">async</span> ( ctx )=&gt;&#123;</span><br><span class="line">ctx.body = <span class="string">'helloworld page!'</span></span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment">// 装载所有子路由</span></span><br><span class="line"><span class="keyword">let</span> router = <span class="keyword">new</span> Router()</span><br><span class="line">router.use(<span class="string">'/'</span>, home.routes(), home.allowedMethods())</span><br><span class="line">router.use(<span class="string">'/page'</span>, page.routes(), page.allowedMethods())</span><br><span class="line"><span class="comment">// 加载路由中间件</span></span><br><span class="line">app.use(router.routes()).use(router.allowedMethods())</span><br><span class="line">app.listen(<span class="number">3000</span>)</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'demo4 is run'</span>)</span><br></pre></td></tr></table></figure>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/06/22/JavaScript的内存管理机制/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/06/22/JavaScript的内存管理机制/" itemprop="url">JavaScript的内存管理机制</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-06-22T16:14:37+08:00">
                2018-06-22
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/JavaScript/" itemprop="url" rel="index">
                    <span itemprop="name">JavaScript</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  1,541
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  6
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>写这篇文章最主要的原因是昨天刷Quero时，看见了一个回答对我一直以来关于JavaScript内存分配上的认识进行了一个补错。再加上最近是考试周了，没有大把时间学习新的东西，因此对Node.js内存管理这块好好学一学，毕竟Node.js的内存管理还是挺重要的。所以综合上面两个原因，先整理一个JS的内存管理机制吧。</p>
<h3 id="一、内存的生命周期"><a href="#一、内存的生命周期" class="headerlink" title="一、内存的生命周期"></a>一、内存的生命周期</h3><p>在JavaScript中，在对内存的使用上一般会经历一个过程，通常来说这个过程是由JavaScript引擎所控制的，而JavaScript引擎中最出名的当属于V8了，在V8的控制下内存一般会有如下的生命周期：</p>
<ol>
<li>内存分配</li>
<li>内存使用</li>
<li>内存回收</li>
</ol>
<h3 id="二、内存分配"><a href="#二、内存分配" class="headerlink" title="二、内存分配"></a>二、内存分配</h3><p>在没有对JavaScript的内存分配进行一个了解之前，我的脑海里只记得这么一句话，但具体在哪看的已经不可考了：在JavaScript中，所有的简单类型会被放在栈中，而所有的引用类型类型会被放在堆中，就像这样：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">// 分配至栈中</span><br><span class="line">var a = 1</span><br><span class="line">var b = &quot;srtian&quot;</span><br><span class="line"></span><br><span class="line">// 分配至堆中</span><br><span class="line">var person = &#123;</span><br><span class="line">    age：22,</span><br><span class="line">    name: &quot;srtian&quot;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这句话在我脑海里面已经有很长时间了，而且我一直认为它没有错（当然这种情况下的确没错）。但就在昨天，我在刷Quero时无意看见了一个回答指出了我对内存分配的认识是错误的：</p>
<blockquote>
<p><a href="https://www.quora.com/JavaScript-programming-language-With-JavaScript-functions-always-acting-as-closures-at-what-point-do-any-variables-get-allocated-to-the-stack-instead-of-the-heap" target="_blank" rel="noopener">Quora</a></p>
</blockquote>
<p>比如下面这段代码：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">var a = 1</span><br><span class="line">var b = 2</span><br><span class="line">var c = &#123;&#125; </span><br><span class="line">var d = function() &#123;</span><br><span class="line">    b = 3</span><br><span class="line">    var f = 4</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p>
<p>按照我原本的认为，其中a,b,f应该存入栈中，而c,d这两个对象则应该存入堆中。但事实并不是这样那个的，那位大佬给的解释是这样的：</p>
<blockquote>
<p>The ‘a’ and ‘f’  variables are stored on the stack because they’re referred only in one depth of closures.<br>‘c’ and ‘d’ despite being ‘local’ are objects (in Javascript function is an object) so they’re put on a heap.<br>And ‘b’ is non-local variable so it’s also put on a heap.</p>
</blockquote>
<p>简单来说，只要在闭包中对一个简单类型进行引用，那么这个简单那类型也会被存放在堆中，而在函数中声明的简单类型则会被存放在栈中，因为它在执行一次后就会被释放。详情可以看大佬们的回答。</p>
<h3 id="三、内存使用"><a href="#三、内存使用" class="headerlink" title="三、内存使用"></a>三、内存使用</h3><p>使用值的过程实际上是对分配内存进行读取与写入的操作。读取与写入可能是写入一个变量或者一个对象的属性值，甚至传递函数的参数。</p>
<h3 id="四、内存回收"><a href="#四、内存回收" class="headerlink" title="四、内存回收"></a>四、内存回收</h3><p>JavaScript具有自动垃圾回收机制，执行环境会负责管理代码执行过程中使用的内存。而这种垃圾收集机制的原理其实很简单，就是找出不在继续使用的变量，然后释放其占用的内存。在JavaScript中垃圾回收机制有两种：</p>
<ol>
<li>标记计数法</li>
<li>引用计数法</li>
</ol>
<h4 id="4-1-标记计数法"><a href="#4-1-标记计数法" class="headerlink" title="4.1 标记计数法"></a>4.1 标记计数法</h4><p>JavaScript 中最常用的垃圾收集方式就是标记清除，大部分的浏览器都使用它。这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。</p>
<p>这个算法假定设置一个叫做根（root）的对象（在Javascript里，根是全局对象）。定期的，垃圾回收器将从根开始，找所有从根开始引用的对象，然后找这些对象引用的对象……从根开始，垃圾回收器将找到所有可以获得的对象和所有不能获得的对象。</p>
<p>从2012年起，所有现代浏览器都使用了标记-清除垃圾回收算法，只是垃圾收集的时间间隔有所不同而已。所有对JavaScript垃圾回收算法的改进都是基于标记-清除算法的改进，并没有改进标记-清除算法本身和它对“对象是否不再需要”的简化定义。</p>
<h4 id="4-2-引用计数法"><a href="#4-2-引用计数法" class="headerlink" title="4.2 引用计数法"></a>4.2 引用计数法</h4><p>引用计数法是一种不太常见的垃圾收集策略。所谓引用计数的含义就是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋给该变量时，则这个值的引用次数就是1。如果同一个变量又被赋给另外一个变量，则该值的引用次数加1。相反，如果包含对着值引用的变量又取得了另一个值，则这个值的引用次数减1。</p>
<p>可以看看MDN上的示例：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> o = &#123; </span><br><span class="line">  a: &#123;</span><br><span class="line">    b:<span class="number">2</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;; </span><br><span class="line"><span class="comment">// 两个对象被创建，一个作为另一个的属性被引用，另一个被分配给变量o</span></span><br><span class="line"><span class="comment">// 很显然，没有一个可以被垃圾收集</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> o2 = o; <span class="comment">// o2变量是第二个对“这个对象”的引用</span></span><br><span class="line"></span><br><span class="line">o = <span class="number">1</span>;      <span class="comment">// 现在，“这个对象”的原始引用o被o2替换了</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> oa = o2.a; <span class="comment">// 引用“这个对象”的a属性</span></span><br><span class="line"><span class="comment">// 现在，“这个对象”有两个引用了，一个是o2，一个是oa</span></span><br><span class="line"></span><br><span class="line">o2 = <span class="string">"yo"</span>; <span class="comment">// 最初的对象现在已经是零引用了</span></span><br><span class="line">           <span class="comment">// 他可以被垃圾回收了</span></span><br><span class="line">           <span class="comment">// 然而它的属性a的对象还在被oa引用，所以还不能回收</span></span><br><span class="line"></span><br><span class="line">oa = <span class="literal">null</span>; <span class="comment">// a属性的那个对象现在也是零引用了</span></span><br><span class="line">           <span class="comment">// 它可以被垃圾回收了</span></span><br></pre></td></tr></table></figure></p>
<p>这种计数法很简单，但却有个限制：无法处理循环引用。在实际开发中，IE 6, 7 （现在已经不考虑他们了）使用引用计数方式对 DOM 对象进行垃圾回收。该方式常常造成对象被循环引用时内存发生泄漏：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> div;</span><br><span class="line"><span class="built_in">window</span>.onload = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  div = <span class="built_in">document</span>.getElementById(<span class="string">"myDivElement"</span>);</span><br><span class="line">  div.circularReference = div;</span><br><span class="line">  div.lotsOfData = <span class="keyword">new</span> <span class="built_in">Array</span>(<span class="number">10000</span>).join(<span class="string">"*"</span>);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p><strong>参考资料:</strong></p>
<p>MDN 内存管理</p>
<p>《JavaScript高级程序设计》</p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/06/19/React 16 生命周期函数浅析/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/06/19/React 16 生命周期函数浅析/" itemprop="url">React 16 生命周期函数浅析</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-06-19T19:41:20+08:00">
                2018-06-19
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/React/" itemprop="url" rel="index">
                    <span itemprop="name">React</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  1,311
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  5
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>刚学 React 的时才 React16.1 刚出来没多久，那时候刚出来一个的钩子函数 componentDidCatch，可应用于处理错误边界。但 React v16.3 又新增了两个生命周期函数，前面写的那篇总结好像有点过时，因此又总结了一下，做个补充。</p>
<h3 id="一、变动情况"><a href="#一、变动情况" class="headerlink" title="一、变动情况"></a>一、变动情况</h3><p>React v16.3 发布时除了全新的 context API 之外，还引入了两个新的生命周期函数：</p>
<ul>
<li>getDerivedStateFromProps</li>
<li>getSnapshotBeforeUpdate</li>
</ul>
<p>并且也确定了在v17.0版本将移除三个生命周期函数：</p>
<ul>
<li>componentWillMount</li>
<li>componentWillReceiveProps</li>
<li>componentWillUpdate。</li>
</ul>
<p>总的来说就是移除了所有带 will 的生命周期函数，也就是 render 之前的生命周期函数除了shouldUpdateComponent 之外的生命周期函数都被干掉了。</p>
<h3 id="二、变动详情"><a href="#二、变动详情" class="headerlink" title="二、变动详情"></a>二、变动详情</h3><p>以我的理解，React 生命周期的变动主要是为了两方面：</p>
<ol>
<li>迎合 Fiber 架构变动的需求</li>
<li>规范开发者开发的行为</li>
</ol>
<h4 id="2-1-getDerivedStateFromProps"><a href="#2-1-getDerivedStateFromProps" class="headerlink" title="2.1 getDerivedStateFromProps"></a>2.1 getDerivedStateFromProps</h4><p>首先以前需要利用被删除的那些生命周期函数才能实现的功能，都可以通过 getDerivedStateProps 的帮助来实现。<br>那 getDerivedStateProps 究竟是啥东西呢？首先 getDerivedStateProps 生命周期函数是一个静态函数，所以函数体内不能访问this。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> getDerivedStateFromProps(nextProps, prevState) &#123;</span><br><span class="line">  <span class="keyword">if</span> (nextProps.translateX !== prevState.translateX) &#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">      translateX: nextProps.translateX,</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这样的函数其实表达的意思很明确，我们在这个函数内部就好好的做个运算就行了，其他的骚操作就别再这做了。</p>
<p>比如很多开发者很喜欢在 componentWillMount  进行AJAX请求以获取数据，因为他们认为 componentWillMount在render 之前执行，早一点执行就早得到结果。但其实在 componentWillMount 中发起 AJAX 请求，不管多快得到结果，都赶不上首次 render 的速度，因此这种操作和可能会导致首屏无数据而导致白屏。</p>
<p>另外对于 React16 架构最大的变动就是 Fiber 了，在 Fiber 架构下启用了启用 async render 之后，render 之前的生命周期函数可能会被调用多次，如果在 componentWillMount 进行 AJAX 请求可能会导致无谓地多次调用AJAX。</p>
<p>其次在 React v16.3 刚发布这个函数的时候，getDerivedStateFromProps 这个生命周期函数，我在从它的名字来看的时候，还以为它主要是为了代替 componentWillReceiveProps 的，但进过了解后发现，这样说其实并不准确。因为 componentWillReceiveProps 只在因为父组件而引发的Updating过程中才会被调用。而getDerivedStateFromProps在Updating和Mounting过程中都会被调用。还需要注意的是，同样是 Updating 过程，如果是因为自身进行的 setState 或者 forceUpdate 所引发的渲染，getDerivedStateFromProps 也不会被调用。</p>
<p>这很容易引发一些问题，且让人难以理解这种差异，毕竟竟然可以在 updating 和 Mounting 过程中都可以调用，那么为什么在 setState 和 forceUpdate 发生时不会调用呢？且如果这样的话，那么在平时使用这个生命周期函数的时候，需不需要单独考虑不调用这个函数的时候需要怎么进行处理，诸如此类的问题还有不少。</p>
<p>也这是由于这个原因，React 团队很快就做出了调整，改正了这一点，让 getDerivedStateFromProps 无论是 Mounting 还是 Updating 还是自身组件的 setstate 或 forceUpdate 都会调用这个函数。这明显到简单多了，非常好理解：只要进行挂载或更新组件，都会调用 getDerivedStateFromProps 生命周期函数。</p>
<h4 id="2-2-getSnapshotBeforeUpdate"><a href="#2-2-getSnapshotBeforeUpdate" class="headerlink" title="2.2  getSnapshotBeforeUpdate"></a>2.2  getSnapshotBeforeUpdate</h4><p>除了 getDerivedStateFromProps 外，React v16.3还引入了一个新的声明周期函数getSnapshotBeforeUpdate。这函数会在render之后执行，而执行之时DOM元素还没有被更新，给了一个机会去获取DOM信息，计算得到一个snapshot，这个snapshot会作为componentDidUpdate的第三个参数传入。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">getSnapshotBeforeUpdate(prevProps, prevState) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'#enter getSnapshotBeforeUpdate'</span>);</span><br><span class="line">  <span class="keyword">return</span> <span class="string">'foo'</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">componentDidUpdate(prevProps, prevState, snapshot) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'#enter componentDidUpdate snapshot = '</span>, snapshot);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>上面这段代码可以看出来这个snapshot怎么个用法，snapshot咋看还以为是组件级别的某个“快照”，其实可以是任何值，到底怎么用完全看开发者自己，getSnapshotBeforeUpdate把snapshot返回，然后DOM改变，然后snapshot传递给componentDidUpdate。</p>
<p>官方给了一个例子，用getSnapshotBeforeUpdate来处理scroll，坦白说，我也想不出其他更常用更好懂的需要getSnapshotBeforeUpdate的例子，这个函数应该大部分开发者都用不上。（这是看程墨大佬的说法抄的，我还是没搞清楚这个函数有啥用。。。o(╥﹏╥)o）</p>
<h4 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h4><p>总的来说这些生命周期函数是 React 团队试图通过框架级别的 API 来约束或者说帮助开发者写出可维护性更佳的 JavaScript 代码。以前一些影响性能的操作，到现在React好像完全不能忍受了，逼着大家写好的代码，这其实挺不错的。</p>
<p>而这些生命周期函数的改动也之一要到React 17 才会进行实装，且那些要移除的生命周期也不会完全废弃，只需要加上UNSATE_的前缀还是可以用的。</p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/06/15/深入理解Redux中间件/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/06/15/深入理解Redux中间件/" itemprop="url">深入理解 Redux 中间件</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-06-15T16:17:30+08:00">
                2018-06-15
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/React/" itemprop="url" rel="index">
                    <span itemprop="name">React</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  2,859
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  11
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>最近几天对 redux 的中间件进行了一番梳理，又看了 redux-saga 的文档，和 redux-thunk 和 redux-promise 的源码，结合前段时间看的redux的源码的一些思考，感觉对 redux 中间件的有了更加深刻的认识，因此总结一下。</p>
<h3 id="一、Redux中间件机制"><a href="#一、Redux中间件机制" class="headerlink" title="一、Redux中间件机制"></a>一、Redux中间件机制</h3><p>Redux本身就提供了非常强大的数据流管理功能，但这并不是它唯一的强大之处，它还提供了利用中间件来扩展自身功能，以满足用户的开发需求。首先我们来看中间件的定义：</p>
<blockquote>
<p>It provides a third-party extension point between dispatching an action, and the moment it reaches<br>the reducer.</p>
</blockquote>
<p>这是Dan Abramov 对 middleware 的描述。简单来讲，Redux middleware 提供了一个分类处理 action 的机会。在 middleware 中，我们可以检阅每一个流过的 action,并挑选出特定类型的 action 进行相应操作，以此来改变 action。这样说起来可能会有点抽象，我们直接来看图，这是在没有中间件情况下的 redux 的数据流：</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/6/15/16402829f39b787c?w=1269&amp;h=157&amp;f=png&amp;s=16392" alt="输入图片说明" title="redux.png"></p>
<p>上面是很典型的一次 redux 的数据流的过程，但在增加了 middleware 后，我们就可以在这途中对 action 进行截获，并进行改变。且由于业务场景的多样性，单纯的修改 dispatch 和 reduce 显然不能满足大家的需要，因此对 redux middleware 的设计理念是可以自由组合，自由插拔的插件机制。也正是由于这个机制，我们在使用 middleware 时，我们可以通过串联不同的 middleware 来满足日常的开发需求，每一个 middleware 都可以处理一个相对独立的业务需求且相互串联：</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/6/15/16402829f54070ae?w=1279&amp;h=239&amp;f=png&amp;s=27633" alt="image"></p>
<p>如上图所示，派发给 redux Store 的 action 对象，会被 Store 上的多个中间件依次处理，如果把 action 和当前的 state 交给 reducer 处理的过程看做默认存在的中间件，那么其实所有的对 action 的处理都可以有中间件组成的。值得注意的是这些中间件会按照指定的顺序依次处理传入的 action，只有排在前面的中间件完成任务后，后面的中间件才有机会继续处理 action，同样的，每个中间件都有自己的“熔断”处理,当它认为这个 action 不需要后面的中间件进行处理时，后面的中间件就不能再对这个 action 进行处理了。</p>
<p>而不同的中间件之所以可以组合使用，是因为 Redux 要求所有的中间件必须提供统一的接口，每个中间件的尉氏县逻辑虽然不一样，但只要遵循统一的接口就能和redux以及其他的中间件对话了。</p>
<h3 id="二、理解中间价的机制"><a href="#二、理解中间价的机制" class="headerlink" title="二、理解中间价的机制"></a>二、理解中间价的机制</h3><p>由于 redux 提供了 applyMiddleware 方法来加载 middleware，因此我们首先可以看一下 redux 中关于 applyMiddleware 的源码：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">applyMiddleware</span>(<span class="params">...middlewares</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="function"><span class="params">createStore</span> =&gt;</span> (...args) =&gt; &#123;</span><br><span class="line">    <span class="comment">// 利用传入的createStore和reducer和创建一个store</span></span><br><span class="line">    <span class="keyword">const</span> store = createStore(...args)</span><br><span class="line">    <span class="keyword">let</span> dispatch = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(</span><br><span class="line">      )</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">const</span> middlewareAPI = &#123;</span><br><span class="line">      getState: store.getState,</span><br><span class="line">      dispatch: <span class="function">(<span class="params">...args</span>) =&gt;</span> dispatch(...args)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 让每个 middleware 带着 middlewareAPI 这个参数分别执行一遍</span></span><br><span class="line">    <span class="keyword">const</span> chain = middlewares.map(<span class="function"><span class="params">middleware</span> =&gt;</span> middleware(middlewareAPI))</span><br><span class="line">    <span class="comment">// 接着 compose 将 chain 中的所有匿名函数，组装成一个新的函数，即新的 dispatch</span></span><br><span class="line">    dispatch = compose(...chain)(store.dispatch)</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">      ...store,</span><br><span class="line">      dispatch</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>我们可以看到applyMiddleware的源码非常简单，但却非常精彩，具体的解读可以看我的这篇文章：<br> <a href="http://srtian96.gitee.io/blog/2018/06/02/%E8%A7%A3%E8%AF%BBRedux%E6%BA%90%E7%A0%81/">redux源码解读</a></p>
<p>从上面的代码我们不难看出，applyMiddleware 这个函数的核心就在于在于组合 compose，通过将不同的 middlewares 一层一层包裹到原生的 dispatch 之上，然后对 middleware 的设计采用柯里化的方式，以便于compose ，从而可以动态产生 next 方法以及保持 store 的一致性。</p>
<p>说起来可能有点绕，直接来看一个啥都不干的中间件是如何实现的：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> doNothingMidddleware = <span class="function">(<span class="params">dispatch, getState</span>) =&gt;</span> next =&gt; <span class="function"><span class="params">action</span> =&gt;</span> next(action)</span><br></pre></td></tr></table></figure>
<p>上面这个函数接受一个对象作为参数，对象的参数上有两个字段 dispatch 和 getState，分别代表着 Redux Store 上的两个同名函数，但需要注意的是并不是所有的中间件都会用到这两个函数。然后 doNothingMidddleware 返回的函数接受一个 next 类型的参数，这个 next 是一个函数，如果调用了它，就代表着这个中间件完成了自己的职能，并将对 action 控制权交予下一个中间件。但需要注意的是，这个函数还不是处理 action 对象的函数，它所返回的那个以 action 为参数的函数才是。最后以 action 为参数的函数对传入的 action 对象进行处理，在这个地方可以进行操作，比如：</p>
<ul>
<li>调动dispatch派发一个新 action 对象</li>
<li>调用 getState 获得当前 Redux Store 上的状态</li>
<li>调用 next 告诉 Redux 当前中间件工作完毕，让 Redux 调用下一个中间件</li>
<li>访问 action 对象 action 上的所有数据</li>
</ul>
<p>在具有上面这些功能后，一个中间件就足够获取 Store 上的所有信息，也具有足够能力可用之数据的流转。看完上面这个最简单的中间件，下面我们来看一下 redux 中间件内，最出名的中间件 redux-thunk 的实现：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">createThunkMiddleware</span>(<span class="params">extraArgument</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="function">(<span class="params">&#123; dispatch, getState &#125;</span>) =&gt;</span> next =&gt; <span class="function"><span class="params">action</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> action === <span class="string">'function'</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> action(dispatch, getState, extraArgument);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> next(action);</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> thunk = createThunkMiddleware();</span><br><span class="line">thunk.withExtraArgument = createThunkMiddleware;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> thunk;</span><br></pre></td></tr></table></figure>
<p>redux-thunk的代码很简单，它通过函数式编程的思想来设计的，它让每个函数的功能都尽可能的小，然后通过函数的嵌套组合来实现复杂的功能，我上面写的那个最简单的中间件也是如此（当然，那是个瓜皮中间件）。redux-thunk 中间件的功能也很简单。首先检查参数 action 的类型，如果是函数的话，就执行这个 action 函数，并把 dispatch, getState, extraArgument 作为参数传递进去，否则就调用 next 让下一个中间件继续处理 action 。</p>
<p>需要注意的是，每个中间件最里层处理 action 参数的函数返回值都会影响 Store 上的 dispatch 函数的返回值，但每个中间件中这个函数返回值可能都不一样。就比如上面这个 react-thunk 中间件，返回的可能是一个 action 函数，也有可能返回的是下一个中间件返回的结果。因此，dispatch 函数调用的返回结果通常是不可控的，我们最好不要依赖于 dispatch 函数的返回值。</p>
<h3 id="三、redux的异步流"><a href="#三、redux的异步流" class="headerlink" title="三、redux的异步流"></a>三、redux的异步流</h3><p>在多种中间件中，处理 redux 异步事件的中间件，绝对占有举足轻重的地位。从简单的 react-thunk 到 redux-promise 再到 redux-saga等等，都代表这各自为解决不同的redux异步流管理问题的方案。</p>
<h4 id="3-1-redux-thunk"><a href="#3-1-redux-thunk" class="headerlink" title="3.1 redux-thunk"></a>3.1 redux-thunk</h4><p>前面我们已经对redux-thunk进行了讨论，它通过多参数的 currying 以实现对函数的惰性求值，从而将同步的 action 转为异步的 action。在理解了redux-thunk后，我们在实现数据请求时，action就可以这么写了：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getWeather</span>(<span class="params">url, params</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="function">(<span class="params">dispatch, getState</span>) =&gt;</span> &#123;</span><br><span class="line">        fetch(url, params)</span><br><span class="line">            .then(<span class="function"><span class="params">result</span> =&gt;</span> &#123;</span><br><span class="line">                dispatch(&#123;</span><br><span class="line">                    type: <span class="string">'GET_WEATHER_SUCCESS'</span>, <span class="attr">payload</span>: result,</span><br><span class="line">                &#125;);</span><br><span class="line">            &#125;)</span><br><span class="line">            .catch(<span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">                dispatch(&#123;</span><br><span class="line">                    type: <span class="string">'GET_WEATHER_ERROR'</span>, <span class="attr">error</span>: err,</span><br><span class="line">                &#125;);</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>尽管redux-thunk很简单，而且也很实用，但人总是有追求的，追求着使用更加优雅的方法来实现redux异步流的控制，这就有了redux-promise。</p>
<h4 id="3-2-redux-promise"><a href="#3-2-redux-promise" class="headerlink" title="3.2 redux-promise"></a>3.2 redux-promise</h4><p>不同的中间件都有着自己的适用场景，react-thunk 比较适合于简单的API请求的场景，而 Promise 则更适合于输入输出操作。而fetch函数返回的结果就是一个Promise对象，下面就让我们来看下最简单的 Promise 对象是怎么实现的：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; isFSA &#125; <span class="keyword">from</span> <span class="string">'flux-standard-action'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isPromise</span>(<span class="params">val</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> val &amp;&amp; <span class="keyword">typeof</span> val.then === <span class="string">'function'</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">promiseMiddleware</span>(<span class="params">&#123; dispatch &#125;</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="function"><span class="params">next</span> =&gt;</span> action =&gt; &#123;</span><br><span class="line">    <span class="keyword">if</span> (!isFSA(action)) &#123;</span><br><span class="line">      <span class="keyword">return</span> isPromise(action)</span><br><span class="line">        ? action.then(dispatch)</span><br><span class="line">        : next(action);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> isPromise(action.payload)</span><br><span class="line">      ? action.payload.then(</span><br><span class="line">          result =&gt; dispatch(&#123; ...action, <span class="attr">payload</span>: result &#125;),</span><br><span class="line">          error =&gt; &#123;</span><br><span class="line">            dispatch(&#123; ...action, <span class="attr">payload</span>: error, <span class="attr">error</span>: <span class="literal">true</span> &#125;);</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">Promise</span>.reject(error);</span><br><span class="line">          &#125;</span><br><span class="line">        )</span><br><span class="line">      : next(action);</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>它的逻辑也很简单主要是下面两部分：</p>
<ol>
<li>先判断是不是标准的 flux action。如果不是，那么判断是否是 promise, 是的话就执行 action.then(dispatch)，否则执行 next(action)。</li>
<li>如果是, 就先判断 payload 是否是 promise，如果是的话 payload.then 获取数据，然后把数据作为 payload 重新 dispatch({ …action, payload: result}) ；不是的话就执行 next(action)</li>
</ol>
<p>结合 redux-promise 我们就可以利用 es7 的 async 和 await 语法，来简化异步操作了，比如这样：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> fetchData = <span class="function">(<span class="params">url, params</span>) =&gt;</span> fetch(url, params)</span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">getWeather</span>(<span class="params">url, params</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> result = <span class="keyword">await</span> fetchData(url, params)</span><br><span class="line">    <span class="keyword">if</span> (result.error) &#123;</span><br><span class="line">        <span class="keyword">return</span> &#123;</span><br><span class="line">            type: <span class="string">'GET_WEATHER_ERROR'</span>, <span class="attr">error</span>: result.error,</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">        <span class="keyword">return</span> &#123;</span><br><span class="line">            type: <span class="string">'GET_WEATHER_SUCCESS'</span>, <span class="attr">payload</span>: result,</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<h4 id="3-3-redux-saga"><a href="#3-3-redux-saga" class="headerlink" title="3.3 redux-saga"></a>3.3 redux-saga</h4><p>redux-saga是一个管理redux应用异步操作的中间件，用于代替 redux-thunk 的。它通过创建 Sagas 将所有异步操作逻辑存放在一个地方进行集中处理，以此将react中的同步操作与异步操作区分开来，以便于后期的管理与维护。对于Saga，我们可简单定义如下：</p>
<blockquote>
<p>Saga = Worker + Watcher</p>
</blockquote>
<p>redux-saga相当于在Redux原有数据流中多了一层，通过对Action进行监听，从而捕获到监听的Action，然后可以派生一个新的任务对state进行维护（这个看项目本身的需求），通过更改的state驱动View的变更。如下图所示：</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/6/15/16402829f9ac4180?w=718&amp;h=428&amp;f=png&amp;s=16806" alt="image"></p>
<p>saga特点：</p>
<ol>
<li>saga 的应用场景是复杂异步。</li>
<li>可以使用 takeEvery 打印 logger（logger大法好），便于测试。</li>
<li>提供 takeLatest/takeEvery/throttle 方法，可以便利的实现对事件的仅关注最近实践还是关注每一次实践的时间限频。</li>
<li>提供 cancel/delay 方法，可以便利的取消或延迟异步请求。</li>
<li>提供 race(effects)，[…effects] 方法来支持竞态和并行场景。</li>
<li>提供 channel 机制支持外部事件。</li>
</ol>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> *<span class="title">getCurrCity</span>(<span class="params">ip</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> data = <span class="keyword">yield</span> call(<span class="string">'/api/getCurrCity.json'</span>, &#123; ip &#125;)</span><br><span class="line">    <span class="keyword">yield</span> put(&#123;</span><br><span class="line">        type: <span class="string">'GET_CITY_SUCCESS'</span>, <span class="attr">payload</span>: data,</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> *<span class="title">getWeather</span>(<span class="params">cityId</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> data = <span class="keyword">yield</span> call(<span class="string">'/api/getWeatherInfo.json'</span>, &#123; cityId &#125;)</span><br><span class="line">    <span class="keyword">yield</span> put(&#123;</span><br><span class="line">        type: <span class="string">'GET_WEATHER_SUCCESS'</span>, <span class="attr">payload</span>: data,</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> *<span class="title">loadInitData</span>(<span class="params">ip</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">yield</span> getCurrCity(ip)</span><br><span class="line">    <span class="keyword">yield</span> getWeather(getCityIdWithState(state))</span><br><span class="line">    <span class="keyword">yield</span> put(&#123;</span><br><span class="line">        type: <span class="string">'GET_DATA_SUCCESS'</span>,</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>总的来讲Redux Saga适用于对事件操作有细粒度需求的场景，同时它也提供了更好的可测试性，与可维护性，比较适合对异步处理要求高的大型项目，而小而简单的项目完全可以使用 redux-thunk 就足以满足自身需求了。毕竟 react-thunk 对于一个项目本身而言，毫无侵入，使用极其简单，只需引入这个中间件就行了。而 react-saga 则要求较高，难度较大，但胜在优雅（虽然我觉得asycn await的写法更优雅）。我现在也并没有掌握和实践这种异步流的管理方式，因此较为底层的东西先就不讨论了。</p>
<p><strong>参考资料：</strong></p>
<ul>
<li>《深入浅出React和Redux》</li>
<li>《深入React技术栈》</li>
</ul>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/06/11/In The Fall/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/06/11/In The Fall/" itemprop="url">In The Fall</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-06-11T22:15:29+08:00">
                2018-06-11
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/随笔/" itemprop="url" rel="index">
                    <span itemprop="name">随笔</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  488
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  2
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <p>今天无意中看到了一个视频：<a href="https://v.qq.com/x/page/q0519cxa22z.html" target="_blank" rel="noopener">In The Fall</a>。大致讲的是一个中年男人在浇花的时候，不慎踩到香蕉皮，然后坠入楼底，在坠入的过程中回忆自己的一生的故事。这本来是很简单的一个过程，但看到这个中年男人在回忆自己的一生时，发现自己从早年的精彩，到中年的单调。最后当他发现自己的生活好像继续下去也没有什么意义时，也就释怀了。</p>
<p>看完这个视频，我突然想到一句话：</p>
<blockquote>
<p>有些人25岁就死了，只是在75岁埋葬。</p>
</blockquote>
<p>心里顿时感觉有点难受，回忆自己的过去，好像也算一个好人吧。跟父母，跟朋友，跟同学，关系都还不错，没有早恋过（已经不可能早恋了），没有做过什么感觉很疯狂的事情，也没有做过什么坏事情。仔细想来唯一值得称道的是，一个人走过不少地方，一路上也认识了不少有意思的人，至于其他的真的是平平淡淡。</p>
<p>思考了很久，就立下以下几个目标吧，希望可以改变一下自己：</p>
<ol>
<li>做一个好人，至少问心无愧。</li>
<li>孝顺父母，每周都和父母聊一聊，哪怕是在微信聊天也行。</li>
<li>善待身边的人，在自己力所能及的地方多去帮助别人，毕竟大家都不容易。</li>
<li>每周除特殊情况至少锻炼3次，毕竟身体健康很重要。</li>
<li>面带微笑，开心点，对自己对他人都好。</li>
<li>每年制定一个对自己挑战，去学一个新的东西。</li>
<li>多看书，以前看书的习惯还得拿起来。</li>
<li>最后，无需去羡慕别人。做自己想做的事，做自己想做的人，就够了。以上！</li>
</ol>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/06/10/Koa2初体验/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/06/10/Koa2初体验/" itemprop="url">Koa2初体验(一)</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-06-10T13:14:36+08:00">
                2018-06-10
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/Node-js/" itemprop="url" rel="index">
                    <span itemprop="name">Node.js</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  1,199
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  6
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="一、起步"><a href="#一、起步" class="headerlink" title="一、起步"></a>一、起步</h3><p>首先创建一个文件夹，然后初始化 package.json ：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm init -y</span><br></pre></td></tr></table></figure>
<p>安装koa2:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cnpm i koa --save</span><br></pre></td></tr></table></figure>
<p>在文件目录下新建一个index.js，然后写下如下代码：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> Koa = <span class="built_in">require</span>(<span class="string">'koa'</span>)</span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Koa()</span><br><span class="line"></span><br><span class="line">app.use( <span class="keyword">async</span>(ctx) =&gt; &#123;</span><br><span class="line">    ctx.body = <span class="string">"hello world"</span></span><br><span class="line">&#125;)</span><br><span class="line">app.listen(<span class="number">1996</span>)</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"demo in run"</span>)</span><br></pre></td></tr></table></figure>
<p>.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 aa<br>然后运行这个文件：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nodemon index.js</span><br></pre></td></tr></table></figure>
<p>然后我们就能在后台看见这个：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">demo is run</span><br></pre></td></tr></table></figure>
<p>然后打开浏览器，输入 ：<a href="http://127.0.0.1:1996" target="_blank" rel="noopener">http://127.0.0.1:1996</a> 就可以看见这个了：</p>
<p><img src="https://gitee.com/uploads/images/2018/0609/235837_aa1db431_1575229.png" alt="输入图片说明"></p>
<p>这样我们就搭建好了最简单的web服务器。但除了这些，有一点需要知道的是，在koa2中，async函数已经大规模使用了，它很好的处理了异步的逻辑，所以学习koa2之前，尽量将async和await解决掉：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> wait1 = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="params">resolve</span> =&gt;</span> &#123;</span><br><span class="line">        resolve(setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;<span class="built_in">console</span>.log(<span class="string">"1s later"</span>)&#125;, <span class="number">1000</span>))</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span>  wait2 = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve</span>) =&gt;</span> &#123;</span><br><span class="line">        resolve(setTimeout(<span class="function"><span class="params">()</span>=&gt;</span>&#123;<span class="built_in">console</span>.log(<span class="string">"2s later"</span>)&#125;,<span class="number">2000</span>))</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">test</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> a = <span class="keyword">await</span> wait1()</span><br><span class="line">    <span class="keyword">const</span> b = <span class="keyword">await</span> wait2()</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">"end"</span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"start"</span>)</span><br><span class="line">test()</span><br></pre></td></tr></table></figure>
<p>上面的代码执行起来就是这样的：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">start</span><br><span class="line">end</span><br><span class="line">1s later</span><br><span class="line">2s later</span><br></pre></td></tr></table></figure>
<p>它很好的解决了异步的一些麻烦，且写出来的代码的可读性也非常好。</p>
<h3 id="二、请求数据获取"><a href="#二、请求数据获取" class="headerlink" title="二、请求数据获取"></a>二、请求数据获取</h3><h4 id="2-1-Get请求的接收"><a href="#2-1-Get请求的接收" class="headerlink" title="2.1 Get请求的接收"></a>2.1 Get请求的接收</h4><p>在Koa2中GET请求可以通过 request 接受收，但接受的方式有两种：</p>
<ul>
<li>query：返回的是格式化后的参数对象</li>
<li>querystring：返回的请求字符串</li>
</ul>
<p>我们可以由两种方式来获取GET请求，一种是通过 ctx.request 来获取GET请求，一种则是直接在ctx中得到GET请求：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> Koa = <span class="built_in">require</span>(<span class="string">'koa'</span>)</span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Koa()</span><br><span class="line"></span><br><span class="line">app.use(<span class="keyword">async</span>(ctx) =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> url = ctx.url</span><br><span class="line">    <span class="comment">// 使用 ctx.request</span></span><br><span class="line">    <span class="keyword">const</span> request = ctx.request</span><br><span class="line">    <span class="keyword">const</span> req_query = request.query</span><br><span class="line">    <span class="keyword">const</span> req_querystring = request.querystring</span><br><span class="line">    <span class="comment">// 直接使用ctx来获取</span></span><br><span class="line">    <span class="keyword">const</span> req_ctx = ctx.query</span><br><span class="line">    <span class="keyword">const</span> req_ctx1 = ctx.querystring</span><br><span class="line">    ctx.body = &#123;</span><br><span class="line">        url,</span><br><span class="line">        req_query,</span><br><span class="line">        req_querystring,</span><br><span class="line">        req_ctx,</span><br><span class="line">        req_ctx1,</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;)</span><br><span class="line">app.listen(<span class="number">3000</span>,() =&gt; &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">"demo1 is run"</span>)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<p>然后我们在浏览器中输入 <a href="http://127.0.0.1:3000?user=srtian&amp;age=18" target="_blank" rel="noopener">http://127.0.0.1:3000?user=srtian&amp;age=18</a> 来访问页面就可以看到这个（这是经过美化的表现）：<br><img src="https://gitee.com/uploads/images/2018/0610/112233_d305d19f_1575229.png" alt="输入图片说明"></p>
<h4 id="2-2-POST请求的接收"><a href="#2-2-POST请求的接收" class="headerlink" title="2.2 POST请求的接收"></a>2.2 POST请求的接收</h4><p>在 Koa2 中，没有给对于 POST 请求的处理封装方便的获取参数的方法，需要通过通过解析上下文 context 中的元素 node.js 请求对象 req 来获取。因此获取POST请求的步骤可以理解为以下三步：</p>
<ol>
<li>解析上下文 ctx 中的原生 node.js 对象 req。</li>
<li>将POST表单数据解析成 query string 字符串。</li>
<li>将字符串转换成 JSON 格式。</li>
</ol>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> Koa = <span class="built_in">require</span>(<span class="string">'koa'</span>)</span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Koa()</span><br><span class="line"></span><br><span class="line">app.use(<span class="keyword">async</span>(ctx) =&gt; &#123;</span><br><span class="line">    <span class="keyword">if</span> (ctx.url === <span class="string">'/'</span> &amp;&amp; ctx.method === <span class="string">'GET'</span>) &#123;</span><br><span class="line">        <span class="keyword">let</span> html = <span class="string">`</span></span><br><span class="line"><span class="string">        &lt;h2&gt;This is demo2&lt;/h2&gt;</span></span><br><span class="line"><span class="string">        &lt;form method="POST" action="/"&gt;</span></span><br><span class="line"><span class="string">            &lt;p&gt;username:&lt;/p&gt;</span></span><br><span class="line"><span class="string">            &lt;input name="username"&gt;</span></span><br><span class="line"><span class="string">            &lt;p&gt;age:&lt;/p&gt;</span></span><br><span class="line"><span class="string">            &lt;input name="age"&gt;</span></span><br><span class="line"><span class="string">            &lt;p&gt;website&lt;/p&gt;</span></span><br><span class="line"><span class="string">            &lt;input name="website"&gt;</span></span><br><span class="line"><span class="string">            &lt;button type="submit"&gt;submit&lt;/button&gt;                   </span></span><br><span class="line"><span class="string">        &lt;/form&gt;</span></span><br><span class="line"><span class="string">        `</span></span><br><span class="line">        ctx.body = html</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (ctx.url === <span class="string">'/'</span> &amp;&amp; ctx.method === <span class="string">'POST'</span>) &#123;</span><br><span class="line">        <span class="keyword">let</span> postData = <span class="keyword">await</span> parsePostDate(ctx)</span><br><span class="line">        ctx.body = postData</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        ctx.body = <span class="string">'&lt;h2&gt;404&lt;/h2&gt;'</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> parsePostDate = <span class="function">(<span class="params">ctx</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">try</span>&#123;</span><br><span class="line">            <span class="keyword">let</span> postData = <span class="string">""</span></span><br><span class="line">            ctx.req.on(<span class="string">'data'</span>, (data) =&gt; &#123;</span><br><span class="line">                postData += data</span><br><span class="line">            &#125;)</span><br><span class="line">            ctx.req.addListener(<span class="string">"end"</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">                <span class="keyword">let</span> parseData = parseQueryStr(postData)</span><br><span class="line">                resolve(parseData)</span><br><span class="line">            &#125;)</span><br><span class="line">        &#125; <span class="keyword">catch</span>(error) &#123;</span><br><span class="line">            reject(error)</span><br><span class="line">         &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> parseQueryStr = <span class="function">(<span class="params">queryStr</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> queryData = &#123;&#125;</span><br><span class="line">    <span class="keyword">const</span> queryStrList = queryStr.split(<span class="string">'&amp;'</span>)</span><br><span class="line">    <span class="built_in">console</span>.log(queryStrList)</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> [index,queryStr] <span class="keyword">of</span> queryStrList.entries()) &#123;</span><br><span class="line">        <span class="keyword">let</span> itemList = queryStr.split(<span class="string">'='</span>)</span><br><span class="line">        <span class="built_in">console</span>.log(itemList)</span><br><span class="line">        queryData[itemList[<span class="number">0</span>]] = <span class="built_in">decodeURIComponent</span>(itemList[<span class="number">1</span>])</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> queryData</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">app.listen(<span class="number">3000</span>, () =&gt; &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'dom2 is run'</span>)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<p>然后打开浏览器，输入<a href="http://127.0.0.1:3000/：" target="_blank" rel="noopener">http://127.0.0.1:3000/：</a><br><img src="https://gitee.com/uploads/images/2018/0610/125659_e92df51e_1575229.png" alt="输入图片说明"></p>
<p>完善信息后，点击submit：</p>
<p><img src="https://gitee.com/uploads/images/2018/0610/125708_aaf5e012_1575229.png" alt="输入图片说明"></p>
<h4 id="koa-bodyparser中间件"><a href="#koa-bodyparser中间件" class="headerlink" title="koa-bodyparser中间件"></a>koa-bodyparser中间件</h4><p>显然上面的 POST 请求的接受非常麻烦，至少对我而言，徒手写个这样的轮子在不查资料的情况下是做不到的，而这样的轮子当然也有人来做，koa-bodyparser就是一个造好的轮子。我们在koa中把这种轮子就叫做中间件。对于POST请求的处理，koa-bodyparser中间件可以把koa2上下文的formData数据解析到ctx.request.body中。</p>
<p>首先我们要安装中间件：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cnpm i koa-bodyparser@3 --save</span><br></pre></td></tr></table></figure>
<p>然后我们就能非常轻松愉快的使用这个中间件来改造我们上面的代码了：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> Koa  = <span class="built_in">require</span>(<span class="string">'koa'</span>)</span><br><span class="line"><span class="keyword">const</span> ap3p = <span class="keyword">new</span> Koa()</span><br><span class="line"><span class="keyword">const</span> bodyParser = <span class="built_in">require</span>(<span class="string">'koa-bodyparser'</span>)</span><br><span class="line"> </span><br><span class="line">app.use(bodyParser())</span><br><span class="line"> </span><br><span class="line">app.use(<span class="keyword">async</span>(ctx) =&gt; &#123;</span><br><span class="line">    <span class="keyword">if</span> (ctx.url === <span class="string">'/'</span> &amp;&amp; ctx.method === <span class="string">'GET'</span>) &#123;</span><br><span class="line">        <span class="keyword">let</span> html = <span class="string">`</span></span><br><span class="line"><span class="string">        &lt;h2&gt;This is demo2&lt;/h2&gt;</span></span><br><span class="line"><span class="string">        &lt;form method="POST" action="/"&gt;</span></span><br><span class="line"><span class="string">            &lt;p&gt;username:&lt;/p&gt;</span></span><br><span class="line"><span class="string">            &lt;input name="username"&gt;</span></span><br><span class="line"><span class="string">            &lt;p&gt;age:&lt;/p&gt;</span></span><br><span class="line"><span class="string">            &lt;input name="age"&gt;</span></span><br><span class="line"><span class="string">            &lt;p&gt;website&lt;/p&gt;</span></span><br><span class="line"><span class="string">            &lt;input name="website"&gt;</span></span><br><span class="line"><span class="string">            &lt;button type="submit"&gt;submit&lt;/button&gt;                 </span></span><br><span class="line"><span class="string">        &lt;/form&gt;</span></span><br><span class="line"><span class="string">        `</span></span><br><span class="line">        ctx.body = html</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (ctx.url === <span class="string">'/'</span> &amp;&amp; ctx.method === <span class="string">'POST'</span>) &#123;</span><br><span class="line">        <span class="keyword">let</span> postData = ctx.request.body</span><br><span class="line">        ctx.body = postData</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        ctx.body = <span class="string">'&lt;h2&gt;404&lt;/h2&gt;'</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line">app.listen(<span class="number">3000</span>, () =&gt; &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'demo2 is run'</span>)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
  </section>

  
  <nav class="pagination">
    <a class="extend prev" rel="prev" href="/blog/page/2/"><i class="fa fa-angle-left"></i></a><a class="page-number" href="/blog/">1</a><a class="page-number" href="/blog/page/2/">2</a><span class="page-number current">3</span><a class="page-number" href="/blog/page/4/">4</a><span class="space">&hellip;</span><a class="page-number" href="/blog/page/6/">6</a><a class="extend next" rel="next" href="/blog/page/4/"><i class="fa fa-angle-right"></i></a>
  </nav>



          </div>
          


          

        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      

      <section class="site-overview-wrap sidebar-panel sidebar-panel-active">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <img class="site-author-image" itemprop="image"
                src="/blog/images/avatar.jpg"
                alt="Srtian" />
            
              <p class="site-author-name" itemprop="name">Srtian</p>
              <p class="site-description motion-element" itemprop="description">JUST DO IT.</p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/blog/archives/">
              
                  <span class="site-state-item-count">59</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/blog/categories/index.html">
                  <span class="site-state-item-count">15</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-tags">
                <a href="/blog/tags/index.html">
                  <span class="site-state-item-count">37</span>
                  <span class="site-state-item-name">标签</span>
                </a>
              </div>
            

          </nav>

          

          
            <div class="links-of-author motion-element">
                
                  <span class="links-of-author-item">
                    <a href="https://gitee.com/srtian96" target="_blank" title="GitHub">
                      
                        <i class="fa fa-fw fa-github"></i>GitHub</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="shenruotian@gmail.com" target="_blank" title="E-Mail">
                      
                        <i class="fa fa-fw fa-envelope"></i>E-Mail</a>
                  </span>
                
            </div>
          

          
          

          
          

          

        </div>
      </section>

      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2019</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Srtian</span>
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js">
</script>
  
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-area-chart"></i>
    </span>
    
      <span class="post-meta-item-text">Site words total count&#58;</span>
    
    <span title="Site words total count">102.8k</span>
  
</div>





        
<div class="busuanzi-count">
  <script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>

  
    <span class="site-uv">
      本站访客数
      <span class="busuanzi-value" id="busuanzi_value_site_uv"></span>
      人次
    </span>
  

  
    <span class="site-pv">
      本站总访问量
      <span class="busuanzi-value" id="busuanzi_value_site_pv"></span>
      次
    </span>
  
</div>








        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/blog/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/blog/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/blog/js/src/motion.js?v=5.1.4"></script>



  
  


  <script type="text/javascript" src="/blog/js/src/affix.js?v=5.1.4"></script>

  <script type="text/javascript" src="/blog/js/src/schemes/pisces.js?v=5.1.4"></script>



  

  


  <script type="text/javascript" src="/blog/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  












  

  <script type="text/javascript">
    // Popup Window;
    var isfetched = false;
    var isXml = true;
    // Search DB path;
    var search_path = "search.xml";
    if (search_path.length === 0) {
      search_path = "search.xml";
    } else if (/json$/i.test(search_path)) {
      isXml = false;
    }
    var path = "/blog/" + search_path;
    // monitor main search box;

    var onPopupClose = function (e) {
      $('.popup').hide();
      $('#local-search-input').val('');
      $('.search-result-list').remove();
      $('#no-result').remove();
      $(".local-search-pop-overlay").remove();
      $('body').css('overflow', '');
    }

    function proceedsearch() {
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay"></div>')
        .css('overflow', 'hidden');
      $('.search-popup-overlay').click(onPopupClose);
      $('.popup').toggle();
      var $localSearchInput = $('#local-search-input');
      $localSearchInput.attr("autocapitalize", "none");
      $localSearchInput.attr("autocorrect", "off");
      $localSearchInput.focus();
    }

    // search function;
    var searchFunc = function(path, search_id, content_id) {
      'use strict';

      // start loading animation
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay">' +
          '<div id="search-loading-icon">' +
          '<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>' +
          '</div>' +
          '</div>')
        .css('overflow', 'hidden');
      $("#search-loading-icon").css('margin', '20% auto 0 auto').css('text-align', 'center');

      $.ajax({
        url: path,
        dataType: isXml ? "xml" : "json",
        async: true,
        success: function(res) {
          // get the contents from search data
          isfetched = true;
          $('.popup').detach().appendTo('.header-inner');
          var datas = isXml ? $("entry", res).map(function() {
            return {
              title: $("title", this).text(),
              content: $("content",this).text(),
              url: $("url" , this).text()
            };
          }).get() : res;
          var input = document.getElementById(search_id);
          var resultContent = document.getElementById(content_id);
          var inputEventFunction = function() {
            var searchText = input.value.trim().toLowerCase();
            var keywords = searchText.split(/[\s\-]+/);
            if (keywords.length > 1) {
              keywords.push(searchText);
            }
            var resultItems = [];
            if (searchText.length > 0) {
              // perform local searching
              datas.forEach(function(data) {
                var isMatch = false;
                var hitCount = 0;
                var searchTextCount = 0;
                var title = data.title.trim();
                var titleInLowerCase = title.toLowerCase();
                var content = data.content.trim().replace(/<[^>]+>/g,"");
                var contentInLowerCase = content.toLowerCase();
                var articleUrl = decodeURIComponent(data.url);
                var indexOfTitle = [];
                var indexOfContent = [];
                // only match articles with not empty titles
                if(title != '') {
                  keywords.forEach(function(keyword) {
                    function getIndexByWord(word, text, caseSensitive) {
                      var wordLen = word.length;
                      if (wordLen === 0) {
                        return [];
                      }
                      var startPosition = 0, position = [], index = [];
                      if (!caseSensitive) {
                        text = text.toLowerCase();
                        word = word.toLowerCase();
                      }
                      while ((position = text.indexOf(word, startPosition)) > -1) {
                        index.push({position: position, word: word});
                        startPosition = position + wordLen;
                      }
                      return index;
                    }

                    indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
                    indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
                  });
                  if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
                    isMatch = true;
                    hitCount = indexOfTitle.length + indexOfContent.length;
                  }
                }

                // show search results

                if (isMatch) {
                  // sort index by position of keyword

                  [indexOfTitle, indexOfContent].forEach(function (index) {
                    index.sort(function (itemLeft, itemRight) {
                      if (itemRight.position !== itemLeft.position) {
                        return itemRight.position - itemLeft.position;
                      } else {
                        return itemLeft.word.length - itemRight.word.length;
                      }
                    });
                  });

                  // merge hits into slices

                  function mergeIntoSlice(text, start, end, index) {
                    var item = index[index.length - 1];
                    var position = item.position;
                    var word = item.word;
                    var hits = [];
                    var searchTextCountInSlice = 0;
                    while (position + word.length <= end && index.length != 0) {
                      if (word === searchText) {
                        searchTextCountInSlice++;
                      }
                      hits.push({position: position, length: word.length});
                      var wordEnd = position + word.length;

                      // move to next position of hit

                      index.pop();
                      while (index.length != 0) {
                        item = index[index.length - 1];
                        position = item.position;
                        word = item.word;
                        if (wordEnd > position) {
                          index.pop();
                        } else {
                          break;
                        }
                      }
                    }
                    searchTextCount += searchTextCountInSlice;
                    return {
                      hits: hits,
                      start: start,
                      end: end,
                      searchTextCount: searchTextCountInSlice
                    };
                  }

                  var slicesOfTitle = [];
                  if (indexOfTitle.length != 0) {
                    slicesOfTitle.push(mergeIntoSlice(title, 0, title.length, indexOfTitle));
                  }

                  var slicesOfContent = [];
                  while (indexOfContent.length != 0) {
                    var item = indexOfContent[indexOfContent.length - 1];
                    var position = item.position;
                    var word = item.word;
                    // cut out 100 characters
                    var start = position - 20;
                    var end = position + 80;
                    if(start < 0){
                      start = 0;
                    }
                    if (end < position + word.length) {
                      end = position + word.length;
                    }
                    if(end > content.length){
                      end = content.length;
                    }
                    slicesOfContent.push(mergeIntoSlice(content, start, end, indexOfContent));
                  }

                  // sort slices in content by search text's count and hits' count

                  slicesOfContent.sort(function (sliceLeft, sliceRight) {
                    if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
                      return sliceRight.searchTextCount - sliceLeft.searchTextCount;
                    } else if (sliceLeft.hits.length !== sliceRight.hits.length) {
                      return sliceRight.hits.length - sliceLeft.hits.length;
                    } else {
                      return sliceLeft.start - sliceRight.start;
                    }
                  });

                  // select top N slices in content

                  var upperBound = parseInt('1');
                  if (upperBound >= 0) {
                    slicesOfContent = slicesOfContent.slice(0, upperBound);
                  }

                  // highlight title and content

                  function highlightKeyword(text, slice) {
                    var result = '';
                    var prevEnd = slice.start;
                    slice.hits.forEach(function (hit) {
                      result += text.substring(prevEnd, hit.position);
                      var end = hit.position + hit.length;
                      result += '<b class="search-keyword">' + text.substring(hit.position, end) + '</b>';
                      prevEnd = end;
                    });
                    result += text.substring(prevEnd, slice.end);
                    return result;
                  }

                  var resultItem = '';

                  if (slicesOfTitle.length != 0) {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + highlightKeyword(title, slicesOfTitle[0]) + "</a>";
                  } else {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + title + "</a>";
                  }

                  slicesOfContent.forEach(function (slice) {
                    resultItem += "<a href='" + articleUrl + "'>" +
                      "<p class=\"search-result\">" + highlightKeyword(content, slice) +
                      "...</p>" + "</a>";
                  });

                  resultItem += "</li>";
                  resultItems.push({
                    item: resultItem,
                    searchTextCount: searchTextCount,
                    hitCount: hitCount,
                    id: resultItems.length
                  });
                }
              })
            };
            if (keywords.length === 1 && keywords[0] === "") {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>'
            } else if (resultItems.length === 0) {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'
            } else {
              resultItems.sort(function (resultLeft, resultRight) {
                if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
                  return resultRight.searchTextCount - resultLeft.searchTextCount;
                } else if (resultLeft.hitCount !== resultRight.hitCount) {
                  return resultRight.hitCount - resultLeft.hitCount;
                } else {
                  return resultRight.id - resultLeft.id;
                }
              });
              var searchResultList = '<ul class=\"search-result-list\">';
              resultItems.forEach(function (result) {
                searchResultList += result.item;
              })
              searchResultList += "</ul>";
              resultContent.innerHTML = searchResultList;
            }
          }

          if ('auto' === 'auto') {
            input.addEventListener('input', inputEventFunction);
          } else {
            $('.search-icon').click(inputEventFunction);
            input.addEventListener('keypress', function (event) {
              if (event.keyCode === 13) {
                inputEventFunction();
              }
            });
          }

          // remove loading animation
          $(".local-search-pop-overlay").remove();
          $('body').css('overflow', '');

          proceedsearch();
        }
      });
    }

    // handle and trigger popup window;
    $('.popup-trigger').click(function(e) {
      e.stopPropagation();
      if (isfetched === false) {
        searchFunc(path, 'local-search-input', 'local-search-result');
      } else {
        proceedsearch();
      };
    });

    $('.popup-btn-close').click(onPopupClose);
    $('.popup').click(function(e){
      e.stopPropagation();
    });
    $(document).on('keyup', function (event) {
      var shouldDismissSearchPopup = event.which === 27 &&
        $('.search-popup').is(':visible');
      if (shouldDismissSearchPopup) {
        onPopupClose();
      }
    });
  </script>





  

  

  

  
  

  

  

  

</body>
</html>
